﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Direct3D10.Graphics.Implementation;
using Tesla.Graphics;
using D3D = SlimDX.Direct3D10;

namespace Tesla.Direct3D10.Graphics {
    /// <summary>
    /// Concrete Direct3D10 implementation of <see cref="IEffectTechnique"/>.
    /// </summary>
    public sealed class D3D10EffectTechnique : IEffectTechnique {
        private String _name;
        private D3D10EffectPassCollection _passes;
        private D3D10EffectAnnotationCollection _annotations;
        private D3D.EffectTechnique _tech;
        private D3D10EffectImplementation _parent;

        /// <summary>
        /// Gets the technique name.
        /// </summary>
        public String Name {
            get {
                return _name;
            }
        }

        /// <summary>
        /// Gets the collection of passes that decorate the technique.
        /// </summary>
        public IEffectPassCollection Passes {
            get {
                return _passes;
            }
        }

        /// <summary>
        /// Gets the collection of annotations contained by the technique.
        /// </summary>
        public IEffectAnnotationCollection Annotations {
            get {
                return _annotations;
            }
        }

        /// <summary>
        /// Gets the implementation parent, so we can quickly compare who this technique belongs to.
        /// </summary>
        internal D3D10EffectImplementation Parent {
            get {
                return _parent;
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10EffectTechnique"/>.
        /// </summary>
        /// <param name="parent">The implementation parent.</param>
        /// <param name="tech">The D3D10 technique.</param>
        internal D3D10EffectTechnique(D3D10EffectImplementation parent, D3D.EffectTechnique tech) {
            _tech = tech;
            _parent = parent;

            D3D.EffectTechniqueDescription desc = _tech.Description;
            _name = desc.Name;

            //Fetch annotations
            int annoCount = desc.AnnotationCount;
            _annotations = new D3D10EffectAnnotationCollection();
            for(int i = 0; i < annoCount; i++) {
                D3D.EffectVariable anno = _tech.GetAnnotationByIndex(i);
                D3D.EffectVariableDescription annoDesc = anno.Description;
                D3D.EffectVariable annoVar = anno.AsString();
                if(annoVar != null) {
                    _annotations.Add(new D3D10EffectAnnotation(annoVar, annoDesc.Name, annoDesc.Semantic, EffectParameterClass.Object, EffectParameterType.String));
                }
                annoVar = anno.AsScalar();
                if(annoVar != null) {
                    //Need to find a way to get the param type...
                    _annotations.Add(new D3D10EffectAnnotation(annoVar, annoDesc.Name, annoDesc.Semantic, EffectParameterClass.Scalar, EffectParameterType.Unknown));
                }
                annoVar = anno.AsMatrix();
                if(annoVar != null) {
                    _annotations.Add(new D3D10EffectAnnotation(annoVar, annoDesc.Name, annoDesc.Semantic, EffectParameterClass.Matrix, EffectParameterType.Single));
                }
            }

            _passes = new D3D10EffectPassCollection();
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10EffectTechnique"/>.
        /// </summary>
        /// <param name="parent">The implementation parent.</param>
        /// <param name="effect">The D3D10 effect.</param>
        /// <param name="tech">The D3D10 technique.</param>
        internal D3D10EffectTechnique(D3D10EffectImplementation parent, D3D.Effect effect, D3D.EffectTechnique tech) {
            _tech = tech;
            _parent = parent;

            D3D.EffectTechniqueDescription desc = _tech.Description;
            _name = desc.Name;

            //Fetch annotations
            int annoCount = desc.AnnotationCount;
            _annotations = new D3D10EffectAnnotationCollection();
            for(int i = 0; i < annoCount; i++) {
                D3D.EffectVariable anno = _tech.GetAnnotationByIndex(i);
                D3D.EffectVariableDescription annoDesc = anno.Description;
                D3D.EffectVariable annoVar = anno.AsString();
                if(annoVar != null) {
                    _annotations.Add(new D3D10EffectAnnotation(annoVar, annoDesc.Name, annoDesc.Semantic, EffectParameterClass.Object, EffectParameterType.String));
                }
                annoVar = anno.AsScalar();
                if(annoVar != null) {
                    //Need to find a way to get the param type...
                    _annotations.Add(new D3D10EffectAnnotation(annoVar, annoDesc.Name, annoDesc.Semantic, EffectParameterClass.Scalar, EffectParameterType.Unknown));
                }
                annoVar = anno.AsMatrix();
                if(annoVar != null) {
                    _annotations.Add(new D3D10EffectAnnotation(annoVar, annoDesc.Name, annoDesc.Semantic, EffectParameterClass.Matrix, EffectParameterType.Single));
                }
            }

            _passes = new D3D10EffectPassCollection();
            for(int i = 0; i < desc.PassCount; i++) {
                _passes.Add(new D3D10EffectPass(parent, effect, tech.GetPassByIndex(i)));
            }
        }

        internal D3D10EffectTechnique Clone(D3D.EffectTechnique tech, D3D10EffectImplementation parent) {
            D3D10EffectTechnique clone = new D3D10EffectTechnique(parent, tech);
            clone._passes = _passes.Clone(tech);
            return clone;
        }
    }
}
